package com.freestyle.common.spring.support;


import com.freestyle.common.db.BeanUtil;
import com.freestyle.common.protocols.ModifyDataException;
import com.freestyle.common.utils.StringUtils;
import com.freestyle.common.utils.Util;
import com.jeesite.autoconfigure.core.CommonAutoConfiguration;
import com.jeesite.common.config.Global;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.persistence.Column;
import javax.persistence.Table;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

@ControllerAdvice
public class ControllerAdviceConfig {
  @Autowired
  protected CommonAutoConfiguration config;
  @Autowired
  protected  DatabaseExceptionConverter databaseExceptionConverter;
  protected MessageSource msgSource;
  public ModifyDataException methodArgumentNotValidExceptionToModifyDataException (MethodArgumentNotValidException pvExp){
    msgSource=config.i18nMessageSource();
    //CommonAutoConfiguration a;
    Object lvTarget = pvExp.getBindingResult().getTarget();
    // Method lvMethod=lvExp.getParameter().getMethod();
    StringBuilder sbMsg = new StringBuilder();
    StringBuilder sbRefs = new StringBuilder();
    for (ObjectError item : pvExp.getBindingResult().getAllErrors()) {
      FieldError lvError = (FieldError) item;
      String lvsDefaultMsg = lvError.getDefaultMessage();
      if (lvsDefaultMsg.startsWith("{")) {
        if (!lvsDefaultMsg.endsWith("}")) {
          throw new RuntimeException("miss '}' with error Message pattern:" + lvsDefaultMsg);
        }
        String lvsTmp = StringUtils.left(lvsDefaultMsg, lvsDefaultMsg.length() - 1);
        String[] lvItems = StringUtils.right(lvsTmp, lvsTmp.length() - 1).split(",");
        if (lvItems.length == 0 || lvItems.length == 1 && lvItems[0].isEmpty()) {
          throw new RuntimeException("error Message pattern is blank");
        }
        String lvsKey = lvItems[0];
        List<Object> lvParams = new ArrayList<Object>();
        for (int i = 1; i < lvItems.length; i++) {
          String lvsParam = lvItems[i];
          if (lvsParam.startsWith("#{") && lvsParam.endsWith("}")) { // #{target.email}
            lvsTmp = StringUtils.left(lvsParam, lvsParam.length() - 1);
            lvsTmp = StringUtils.right(lvsTmp, lvsTmp.length() - 2);
            if (lvsTmp.equals("label")) {
              Table lvEntity= lvTarget.getClass().getAnnotation(Table.class);
              if (lvEntity!=null) {
                String lvsTableName=lvEntity.name();
                String lvT1[]=lvsTableName.split("\\.");
                String lvsResKey=lvT1[lvT1.length-1]+".";
                Column lvCol=null;
                try {
                  lvCol = lvTarget.getClass().getDeclaredField(lvError.getField()).getAnnotation(Column.class);
                } catch (NoSuchFieldException | SecurityException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
                }
                if (lvCol!=null) {
                  lvsResKey=lvsResKey+lvCol.name();
                }
                else {
                  lvsResKey=lvsResKey+lvError.getField();
                }
                lvParams.add(Global.getText(lvsResKey));

              }
            }
            else {
              String[] lvWd = lvsTmp.split("\\.");
              String lvsInst = lvWd[0];
              String lvsField = lvWd[1];
              Object lvInst = null;
              if (lvsInst.equals("target")) {
                lvInst = lvTarget;
                if (lvInst == null) {
                  throw new RuntimeException("Invalid error message param:'" + lvsParam + "'");
                }
                Object lvParamVal = BeanUtil.getFieldValue(lvInst, lvsField, Object.class);
                lvParams.add(lvParamVal);
              } else if (lvsInst.equals("self")) {
                try {
                  Field lvField = lvTarget.getClass().getDeclaredField(lvError.getField());
                  String lvsAnnName = lvError.getCodes()[lvError.getCodes().length-1];
                  Annotation lvAnn = null;
                  for (Annotation ann : lvField.getAnnotations()) {
                    String lvsClName = ann.annotationType().getSimpleName();
                    if (lvsClName.equals(lvsAnnName)) {
                      lvAnn = ann;
                      break;
                    }
                  }
                  if (lvAnn == null) {
                    throw new RuntimeException("Invalid error message param:'" + lvsParam + "'");
                  }
                  Map<String, Object> lvAttrs = AnnotationUtils.getAnnotationAttributes(lvAnn);
                  Object lvParamVal = lvAttrs.get(lvsField);
                  if (lvParamVal instanceof String[]) {
                    lvParamVal="[\""+ Util.toString((String[])lvParamVal,"\",\"")+"\"]";
                  }
                  else if (lvParamVal instanceof int[]) {
                    List<String> lvTLst=new ArrayList<String>();
                    for (int v:(int[]) lvParamVal) {
                      lvTLst.add(""+v);
                    }
                    lvParamVal="["+Util.toString(lvTLst,",")+"]";
                  }
                  lvParams.add(lvParamVal);
                } catch (NoSuchFieldException | SecurityException e1) {
                  throw new RuntimeException(e1);
                }

              }
            }
          } else {
            lvParams.add(lvsParam);
          }

        }
        sbMsg.append("\n" + msgSource.getMessage(lvsKey,
                lvParams.toArray(new Object[lvParams.size()]),Locale.CHINA));
      } else {
        sbMsg.append("\n" + lvsDefaultMsg);
      }
      String lvFieldName = lvError.getField();
      @SuppressWarnings("rawtypes")
      Class lvCz = pvExp.getBindingResult().getTarget().getClass();
      Field lvF;
      try {
        lvF = lvCz.getField(lvFieldName);
        Column lvCol = lvF.getAnnotation(Column.class);
        if (lvCol != null) {
          lvFieldName = lvCol.name();
        }
        sbRefs.append("," + lvFieldName);
      } catch (NoSuchFieldException | SecurityException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
      }

    }
    if (sbRefs.length() > 0) {
      sbRefs.delete(0, 1);
    }
    if (sbMsg.length() > 0) {
      sbMsg.delete(0, 1);
    }
    return new ModifyDataException(sbMsg.toString(), sbRefs.toString().split(","));
  }
  /****
   * 全局异常处理:全部以Json格式返回至前端, 并且在日志中打印调用堆栈
   *
   * @param e
   * @return
   */
  @ExceptionHandler(value = Exception.class)
  public @ResponseBody
  Exception handleException(Exception e) {
    if (e instanceof DuplicateKeyException){
       return databaseExceptionConverter.handleDuplateKeyException((DuplicateKeyException) e);
    }
    if (e instanceof DataIntegrityViolationException){
      return databaseExceptionConverter.handleDataIntegrityViolationException((DataIntegrityViolationException) e);
    }
    if (!(e instanceof ModifyDataException || e instanceof MethodArgumentNotValidException)) {
      e.printStackTrace();
    }
    if (e instanceof MethodArgumentNotValidException) {
      MethodArgumentNotValidException lvExp = (MethodArgumentNotValidException) e;
      e =methodArgumentNotValidExceptionToModifyDataException(lvExp);
    }
    return e;
  }
}
